from google.appengine.ext import db
from google.appengine.ext import webapp
from google.appengine.api import users
from google.appengine.api import urlfetch

import utils
import logging
import urllib
import yaml
import settings
import cgi
import urllib
import json
import random
import string

class Singleton(db.Model):
    secret        = db.StringProperty()
    channel_id    = db.StringProperty()
    event_key     = db.StringProperty()
    destroy_key   = db.StringProperty()
    logged_users  = db.ListProperty(unicode)
    event_id      = db.IntegerProperty()
    events        = db.ListProperty(unicode)

def get_singleton():
    s = Singleton.all().fetch(1)
    if len(s) < 1:
        s = Singleton()
        s.secret = utils.random_string()
        s.channel_id = 'notset'
        s.save()
        s = Singleton.all().fetch(1)
        assert s
    s = s[0]
    return s

class Main(webapp.RequestHandler):
    def get(self):
        username = self.request.GET.get('username','')
        username = filter(lambda c: c in (string.ascii_letters + string.digits + '-_'), username)
        if not username:
            username = 'user_' + utils.random_string(length=4)
        
        self.response.out.write(utils.render_to_mako('shoutbox/shoutbox_main.html', 
            {'servicesurl':settings.SERVICESURL, 
            'domain':settings.TOPDOMAIN,
            'username':username,
            }
            ))

class ChannelUri(webapp.RequestHandler):
    def get(self):
        s = get_singleton()
        try:
            resp = urlfetch.fetch('http://comet.%s/comet/%s/?rand=%f' % (settings.SERVICESURL, s.channel_id, random.random() ), method=urlfetch.GET, headers={}, allow_truncated=True)
        except urlfetch.DownloadError:
            resp = None
        if resp and resp.status_code != 200:
            uri = 'http://%s/shoutbox/user_action?secret=%s' % (settings.DOMAIN, s.secret)
            q = urllib.urlencode({
                'public':      'True',
                'event_callback_uri':uri,
                'login_callback_uri':uri,
                'logout_callback_uri':uri,
                'disconnect_time': 8,
                #'keepalive_time':30,
            })
            
            resp = urlfetch.fetch('http://comet.%s/comet/create_channel' % (settings.SERVICESURL), payload=q, method=urlfetch.POST, headers={}, allow_truncated=True)
            if resp.status_code != 200:
                logging.error("creating channel failed %.250r!" % (q, resp.content) )
                return
        
            dd = yaml.load(resp.content)
            
            def set_channel_id(key, amount):
                obj = db.get(key)
                # noone haven't modified the record?
                if s.channel_id == obj.channel_id:
                    obj.channel_id = dd['channel_id']
                    obj.event_key  = dd['event_key']
                    obj.destroy_key= dd['destroy_key']
                    obj.logged_users = []
                    obj.events       = []
                    obj.event_id   = 0
                obj.put()
            db.run_in_transaction(set_channel_id, s.key(), 5)
            s = db.get(s.key())
        
        o = {
            'channel_id': s.channel_id,
            'userlist': map(lambda a:a.encode('utf-8'), s.logged_users),
            'events':   map(lambda a:a.encode('utf-8'), s.events),
            'max_id':   s.event_id,
        }
        self.response.out.write(json.write(o))

class Push(webapp.RequestHandler):
    def post(self):
        s = get_singleton()
        data   = self.request.POST.get('data', '')
        username   = self.request.POST.get('username', 'unknown')
        if data:
            send_event(s.channel_id, '%s: %s' % (username, data), s.event_key)
        

def send_event(channel_id, msg, event_key):
    q = urllib.urlencode({
        'data':      msg,
        'event_key': event_key,
    })
    resp = urlfetch.fetch('http://comet.%s/comet/%s/event' % (settings.SERVICESURL, channel_id), payload=q, method=urlfetch.POST, headers={}, allow_truncated=False)
    if resp.status_code != 200:
        logging.error("sending event failed to ch %s" % channel_id)
    
    o = yaml.load(resp.content)
    return o['event_id']
    
    

class UserAction(webapp.RequestHandler):
    def get(self):
        action   = self.request.GET.get('action', None)
        secret   = self.request.GET.get('secret', None)
        channel_id   = self.request.GET.get('channel_id', None)
        if not action:
            return
        
        s = get_singleton()
        if secret != s.secret:
            return
        
        assert s.channel_id == channel_id
        
        if action == 'event':
            event_id   = self.request.GET.get('event_id', None)
            data       = self.request.GET.get('data', None)
            if not event_id or not data:
                return
            
            s.event_id = int(event_id)
            s.events.append(data)
            # keep last 15 events
            s.events = s.events[-15:]
        
        username = self.request.GET.get('username', None)
        
        if action == 'login':
            if not username:
                return
            s.logged_users.append(username)
            s.event_id = send_event(s.channel_id, 'login %s' % username, s.event_key)
        elif action == 'logout':
            if not username:
                return
            if username in s.logged_users:
                s.logged_users.remove(username)
                s.save()
            else:
                logging.warning('user %r not loggedin and wants tologout' % username)
            
            s.event_id = send_event(s.channel_id, 'logout %s' % username, s.event_key)
        s.save()
        return

